<?php
/**
 * 汇友科技
 * @copyright (c) 2017 http://www.huiyou365.net All rights reserved.
 * @author: Creatint <creatint@163.com>
 */

namespace Common\Container;

use Think\Controller;
use Closure;
use ArrayAccess;
use ReflectionClass;
use Think\Exception;

class Container extends Controller implements ArrayAccess {


    /**
     * 别名映射表
     *
     * @var array
     */
    protected $aliases = array();

    /**
     * 已绑定的名称.
     *
     * @var array
     */
    protected $bindings = array();

    /**
     * 容器内的共享实例
     *
     * @var array
     */
    protected $instances = [];

    /**
     * 实例初始化参数
     * @var array
     */
    protected $params = [];



    public function _initialize(){

    }
    public function params($params){
        $this->params = $params;
    }

    // 获取abstract的实例
    public function make( $abstract ,array $parameters = array()){
        // 获取全名
        $abstract = $this->getAlias($abstract);

        // 获取实例
        if (isset($this->instances[$abstract])) {
//            trace('存在实例: '.$abstract,'container');
            return $this->instances[$abstract];
        } else {
//            trace('不存在实例: '.$abstract,'container');
        }

        $object = $this->build($abstract);

        $this->instances[$abstract] = $object;

//        // 共享实例
//        if ( $object = new $abstract() ) {
//            $this->instances[$abstract] = $object;
//        }

        return $object;
    }

    public function build($abstract)
    {
        $object = null;
        try {
            $reflector = new ReflectionClass($abstract);
            if (!$reflector->isInstantiable()) {
                $object = D($abstract);
            } else {
                $object = $reflector->newInstanceArgs($this->params);
            }
        } catch (\Exception $e) {
            // 无法直接实例化的名称，会抛出异常，此时用D方法获取实例
//            trace('获取反射异常','container');
            $object = D($abstract);
        }
//        trace('创建实例：'.$abstract,'container');
        return $object;

    }

    /**
     * 从别名映射表中获取目标名称.
     *
     * @param string $abstract
     * @return mixed
     */
    public function getAlias( $abstract )
    {
        if ( ! isset($this->aliases[$abstract]) ) {
            return $abstract;
        }

        if ( $this->aliases[$abstract] == $abstract ) {
//             TODO:throw new LogicExcetion();
        }

        return $this->getAlias($this->aliases[$abstract]);
    }


    public function offsetExists($key)
    {
        return $this->bound($key);
    }

    public function offsetGet($key)
    {
        return $this->make($key);
    }
    public function offsetSet($key, $value)
    {
        if ( ! $value instanceof Closure ) {
            $value = function () use ($value) {
                return $value;
            };
        }
        $this->bind($key, $value);
    }
    public function offsetUnset($key)
    {
        $key = $this->normalize($key);
        unset($this->bindings[$key]);
    }

}